跳到主要内容

Obsidian 插件

title: 状态分级图例
collapse: true

- [=] 已知
- [x] 已启用
- [X] 必备
- [P] 推荐
- [/] 可选
- [>] 未启用
- [+] 备用
- [?] 待定
- [<] 未安装
- [_] 忽略
- [C] 不推荐
- [-] 废弃
- [ ] 未知

本体类

%% 对核心功能进行改进 %%

  • better-markdown-links
  • [P] css-editor
  • custom-sort
  • fuzzy-chinese-pinyin
  • notebook-navigator
  • obsidian-front-matter-title-plugin
  • virtual-linker

界面类

%% 对主程序交互界面进行改进 %%

  • cmdr
  • [P] float-search
  • [P] floating-toc
  • [P] note-toolbar
  • obsidian-hover-editor
  • [P] obsidian-style-settings

编辑类

%% 增强编辑体验 %%

  • easy-typing-obsidian
  • [P] editing-toolbar
  • obsidian-heading-shifter
  • [P] obsidian-latex-suite
  • obsidian-linter
  • obsidian-outliner
  • [P] paste-link
  • table-editor-obsidian
  • various-complements

渲染类

%% 影响笔记内容和预览显示效果 %%

  • [P] any-block
  • codeblock-customizer
  • [P] dataview
  • [P] obsidian-admonition

扩展类

%% 带来新的常驻功能玩法 %%

  • [P] advanced-canvas
  • [P] obsidian-excalidraw-plugin
  • [P] obsidian-kanban
  • [P] obsidian-tasks-plugin
  • quickadd
  • [P] templater-obsidian

辅助类

%% 提供可按需运行的辅助工具命令 %%

  • [P] image-converter
  • obsidian-image-toolkit

备用待定

  • Obsidian-sidebar-expand-on-hover-Plus
  • ace-code-editor
  • any-block-pro
  • auto-embed
  • better-search-views
  • block-link-plus
  • breadcrumbs
  • buttons
  • canvas-mindmap
  • cm-chs-patch
  • conditional-properties
  • consistent-attachments-and-links
  • continuous-mode
  • css-inlay-colors
  • custom-commands
  • custom-theme-studio
  • daily-note-calendar
  • daily-notes-editor
  • darlal-switcher-plus
  • datacore
  • datepicker
  • drawio-obsidian
  • dust-calendar
  • enhanced-annotations
  • extended-graph
  • external-rename-handler
  • file-tree-alternative
  • fix-require-modules
  • floating-settings
  • folder-notes
  • frontmatter-modified-date
  • gridexplorer
  • iconic
  • journals
  • js-engine
  • keyshots
  • legacy-vault-switcher
  • lineage
  • linked-note-exporter
  • longform
  • make-md
  • markdown-table-editor
  • markwhen
  • media-extended
  • mermaid-tools
  • metadata-menu
  • modal-opener
  • modalforms
  • mrj-text-expand
  • note-definitions
  • note-status
  • note-to-mp
  • novel-word-count
  • obsidian-custom-attachment-location
  • obsidian-day-planner
  • obsidian-douban-plugin
  • obsidian-file-cooker
  • obsidian-git
  • obsidian-importer
  • obsidian-livesync
  • obsidian-local-images-plus
  • obsidian-markmind
  • obsidian-memos
  • obsidian-meta-bind-plugin
  • obsidian-mindmap-nextgen
  • obsidian-minimal-settings
  • obsidian-plugin-proxy
  • obsidian-plugin-update-tracker
  • obsidian-quiet-outline
  • obsidian-shellcommands
  • obsidian-spaced-repetition
  • obsidian-style-settings
  • obsidian-tagfolder
  • obsidian-task-progress-bar
  • obsidian-tracker
  • obsidian-weread-plugin
  • obsidian42-brat
  • obsidian42-strange-new-worlds
  • outliner-md
  • pane-relief
  • pdf-plus
  • pexels-banner
  • pretty-properties
  • quick-plugin-switcher
  • quicklink
  • recent-files-obsidian
  • refresh-preview
  • remotely-save
  • share-to-notionnext
  • sheet-plus
  • shiki-highlighter
  • sidebar-highlights
  • simple-canvasearch
  • slash-commander
  • sticky-notes
  • supercharged-links-obsidian
  • tag-buddy
  • tag-wrangler
  • task-board
  • tasknotes
  • terminal
  • theme-picker
  • time-ruler
  • typewriter-mode
  • unitade
  • vare
  • vault-explorer
  • vcf-contacts
  • version-control
  • vertical-tabs
  • waypoint
  • webpage-html-export
  • zettelflow

参考

插件笔记

[!note]- 命名规范

  • filename: 参考插件目录名, 例如 'obsidian-xxx-plugin'
  • title: 插件显示名称 name
  • aliases: 插件 id (与 filename 相同则省略) 和 name
views:
- type: list
name: plugins
filters:
and:
- file.hasTag("Obsidian/Plugins")
sort: []

已安装插件解析方法

community-plugins.json 和 plugins 目录解析
cd .obsidian
# 解析已启用的插件
cat community-plugins.json | grep -Ev '\[|\]' | sed 's/[[:space:]]//g' | sed 's/"//g' | sed 's/,//g' | sort | tee enabled-plugins >(pbcopy)
# jq 'sort' community-plugins.json | sed '1d;$d'
# jq '(.desktop.plugins | keys)|.[]' plugins/lazy-plugins/data.json | sed 's/"//g'
# (可选)以多列的方式呈现
# cat enabled-plugins | xargs -n3 | column -t
# 列出已安装的插件(默认已有序)
gfind plugins -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort > all-installed-plugins
# 仅列出未启用的插件
grep -wvf enabled-plugins all-installed-plugins
dv.table(["id", "name", "version", "enabled"], Object.values(app.plugins.manifests).sort((a,b)=>a.id.localeCompare(b.id)).map(p=>[p.id, p.name, p.version, app.plugins.plugins[p.id] !== undefined]))

Better Plugins Manager

社区插件列表解析

jq
jq -n --slurpfile stats Misc/community-plugin-stats.json --slurpfile info Misc/Data/community-plugins.json '
$info[0] | map(. + ($stats[0][.id] // {}))
| map(select(.downloads > 100000))
| sort_by(-.downloads)
| .[0:100][]
| {id: .id, downloads: .downloads}
' | jq -c
simplest impl. render by datacore
const PLUGINS_URL = "https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugins.json";
const STATS_URL = "https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugin-stats.json";

async function main() {
const { manifests: installed, plugins: enabled } = app.plugins;

const [plugins = [], stats = {}] = await Promise.all([
fetch(PLUGINS_URL).then(r => r.json()).catch(() => []),
fetch(STATS_URL).then(r => r.json()).catch(() => ({})),
]);

const oneYearAgo = moment().subtract(1, "years");
const filtered = plugins
.map((p, i) => ({
...p,
index: i,
downloads: stats[p.id]?.downloads || 0,
updated: stats[p.id]?.updated || "",
state: enabled[p.id] ? "**已启用**" : installed[p.id] ? "未启用" : "未安装",
}))
.filter(p =>
p.id in installed ||
(moment(p.updated).isAfter(oneYearAgo) && p.downloads > 10000)
)
.sort((a, b) => b.downloads - a.downloads || a.id.localeCompare(b.id));

return (
<dc.Table
rows={filtered}
columns={[
{id: 'name', value: p => `[${p.name}](https://github.com/${p.repo})`},
{id: 'updated', value: p => p.updated ? moment(p.updated).fromNow() : ""},
{id: 'downloads', value: p => p.downloads.toLocaleString()},
{id: 'state', value: p => p.state},
]}
paging={50}
/>
);
}

return await main();
const installed = app.plugins.manifests;
const enabled = app.plugins.plugins;

const DATA_MAP = {
plugins: { path: "Misc/community-plugins.json", url: "https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugins.json" },
stats: { path: "Misc/community-plugin-stats.json", url: "https://raw.githubusercontent.com/obsidianmd/obsidian-releases/master/community-plugin-stats.json" },
};

const CACHE_AGE = 6048e5; // 7 天
const FETCH_TIMEOUT = 30000;

const fetchData = async (url, t = FETCH_TIMEOUT) => {
const c = new AbortController(), id = setTimeout(() => c.abort(), t);
try {
const r = await fetch(url, { signal: c.signal });
if (!r.ok) throw r.status;
return await r.json();
} finally {
clearTimeout(id);
}
};

const saveData = async (path, obj) => {
try { await app.vault.adapter.write(path, JSON.stringify(obj)); return true; } catch { return false; }
};

const getDataFromCache = async (path) => {
try { return JSON.parse(await app.vault.adapter.read(path)); } catch { return null; }
};

const isCacheStale = async (path, maxAge = CACHE_AGE) =>
app.vault.adapter.stat(path).then(s => Date.now() - s.mtime > maxAge).catch(() => true);

// 智能获取(优先未过期缓存,否则拉取并保存,失败回退缓存或默认)
async function getData({ useCache = true, force = false } = {}) {
const out = {};
await Promise.all(Object.entries(DATA_MAP).map(async ([k, { path, url }]) => {
const need = force || !useCache || await isCacheStale(path);
if (!need) {
const c = await getDataFromCache(path);
if (c != null) { out[`${k}Data`] = c; return; }
}
try {
const remote = await fetchData(url);
out[`${k}Data`] = remote;
saveData(path, remote); // best-effort
new Notice(`${k} 更新`);
} catch (e) {
const fallback = await getDataFromCache(path);
out[`${k}Data`] = fallback != null ? fallback : (k === "plugins" ? [] : {});
console.warn(`[getData] ${k} fetch failed:`, e);
}
}));
return out;
}

// 将 plugins + stats 合并成可直接渲染的行对象(独立函数)
function mergeData(plugins = [], stats = {}, installedMap = installed, enabledMap = enabled) {
return (plugins || []).map((p, i) => ({
...p,
index: i,
downloads: stats[p.id]?.downloads || 0,
updated: stats[p.id]?.updated || "",
state: Object.keys(enabledMap).includes(p.id) ? "**已启用**"
: Object.keys(installedMap).includes(p.id) ? "未启用" : "未安装",
}));
}

// 预设查询
const queryPresets = {
popular: (data) =>
data
.filter(
(p) =>
p.id in installed ||
(moment(p.updated).isAfter(moment().subtract(1, "years")) &&
p.downloads > 10000),
)
.sort(
(a, b) => b.downloads - a.downloads || a.id.localeCompare(b.id),
),
new: (data) =>
data
.filter((p) => p.downloads > 1000)
.slice(-100)
.toReversed(),
canvas: (data) =>
data.filter((p) =>
["id", "name", "description"].some((k) =>
String(p[k] || "")
.toLowerCase()
.includes("canvas"),
),
),
};

// 渲染表格
const renderTable = (title, rows, fields = ["name","updated","downloads","state"]) => {
dv.header(3, title);
dv.table(fields, rows.map(p => [
`[${p.name}](https://github.com/${p.repo})`,
p.updated ? moment(p.updated).fromNow() : "",
p.downloads.toLocaleString(),
p.state
]));
};

// 主流程:获取 -> 合并 -> 多个查询 -> 渲染
async function main() {
const { pluginsData = [], statsData = {} } = await getData();
const merged = mergeData(pluginsData, statsData);

renderTable("Popular", queryPresets.popular(merged));
renderTable("New", queryPresets.new(merged));
renderTable("Canvas", queryPresets.canvas(merged));
}

await main();